"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateConfigWithAccessToken = exports.authorizedScopesForPortalAndUser = exports.scopesOnAccessToken = exports.enabledFeaturesForPersonalAccessKey = exports.accessTokenForPersonalAccessKey = exports.getAccessToken = void 0;
const moment_1 = __importDefault(require("moment"));
const environments_1 = require("../constants/environments");
const auth_1 = require("../constants/auth");
const localDevAuth_1 = require("../api/localDevAuth");
const sandboxHubs_1 = require("../api/sandboxHubs");
const config_1 = require("../config");
const config_2 = require("../constants/config");
const developerTestAccounts_1 = require("../api/developerTestAccounts");
const logger_1 = require("./logger");
const lang_1 = require("../utils/lang");
const errors_1 = require("../errors");
const i18nKey = 'lib.personalAccessKey';
const refreshRequests = new Map();
function getRefreshKey(personalAccessKey, expiration) {
    return `${personalAccessKey}-${expiration || 'fresh'}`;
}
async function getAccessToken(personalAccessKey, env = environments_1.ENVIRONMENTS.PROD, accountId) {
    const axiosResponse = await (0, localDevAuth_1.fetchAccessToken)(personalAccessKey, env, accountId);
    const response = axiosResponse.data;
    return {
        portalId: response.hubId,
        accessToken: response.oauthAccessToken,
        expiresAt: (0, moment_1.default)(response.expiresAtMillis).toISOString(),
        scopeGroups: response.scopeGroups,
        enabledFeatures: response.enabledFeatures,
        encodedOAuthRefreshToken: response.encodedOAuthRefreshToken,
        hubName: response.hubName,
        accountType: response.accountType,
    };
}
exports.getAccessToken = getAccessToken;
async function refreshAccessToken(account) {
    const { personalAccessKey, env, accountId } = account;
    const accessTokenResponse = await getAccessToken(personalAccessKey, env, accountId);
    const { accessToken, expiresAt } = accessTokenResponse;
    (0, config_1.updateConfigAccount)({
        ...account,
        auth: {
            tokenInfo: {
                accessToken,
                expiresAt: expiresAt,
            },
        },
    });
    return accessTokenResponse;
}
async function getNewAccessToken(account) {
    const { personalAccessKey, auth } = account;
    const key = getRefreshKey(personalAccessKey, auth.tokenInfo.expiresAt);
    if (refreshRequests.has(key)) {
        return refreshRequests.get(key);
    }
    let accessTokenResponse;
    try {
        const refreshAccessPromise = refreshAccessToken(account);
        if (key) {
            refreshRequests.set(key, refreshAccessPromise);
        }
        accessTokenResponse = await refreshAccessPromise;
    }
    catch (e) {
        if (key) {
            refreshRequests.delete(key);
        }
        throw e;
    }
    return accessTokenResponse;
}
async function getNewAccessTokenByAccountId(accountId) {
    const account = (0, config_1.getConfigAccountById)(accountId);
    if (!account) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.accountNotFound`, { accountId }));
    }
    if (account.authType !== auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.invalidAuthType`, {
            accountId,
        }));
    }
    const accessTokenResponse = await getNewAccessToken(account);
    return accessTokenResponse;
}
async function accessTokenForPersonalAccessKey(accountId, forceRefresh = false) {
    const account = (0, config_1.getConfigAccountById)(accountId);
    if (!account) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.accountNotFound`, { accountId }));
    }
    if (account.authType !== auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.invalidAuthType`, {
            accountId,
        }));
    }
    const { auth } = account;
    const authTokenInfo = auth && auth.tokenInfo;
    const authDataExists = authTokenInfo && auth?.tokenInfo?.accessToken;
    if (!authDataExists ||
        forceRefresh ||
        (0, moment_1.default)().add(5, 'minutes').isAfter((0, moment_1.default)(authTokenInfo.expiresAt))) {
        return getNewAccessToken(account).then(tokenInfo => tokenInfo.accessToken);
    }
    return auth.tokenInfo?.accessToken;
}
exports.accessTokenForPersonalAccessKey = accessTokenForPersonalAccessKey;
async function enabledFeaturesForPersonalAccessKey(accountId) {
    const accessTokenResponse = await getNewAccessTokenByAccountId(accountId);
    return accessTokenResponse?.enabledFeatures;
}
exports.enabledFeaturesForPersonalAccessKey = enabledFeaturesForPersonalAccessKey;
async function scopesOnAccessToken(accountId) {
    return (await getNewAccessTokenByAccountId(accountId)).scopeGroups;
}
exports.scopesOnAccessToken = scopesOnAccessToken;
async function authorizedScopesForPortalAndUser(accountId) {
    return (await (0, localDevAuth_1.fetchScopeAuthorizationData)(accountId)).data.results;
}
exports.authorizedScopesForPortalAndUser = authorizedScopesForPortalAndUser;
async function updateConfigWithAccessToken(token, personalAccessKey, env, name, makeDefault = false) {
    const { portalId, accessToken, expiresAt, accountType } = token;
    const account = (0, config_1.getConfigAccountIfExists)(portalId);
    const accountEnv = env || account?.env || environments_1.ENVIRONMENTS.PROD;
    let parentAccountId;
    try {
        if (accountType === config_2.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX ||
            accountType === config_2.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX) {
            const { data: sandboxDataResponse } = await (0, sandboxHubs_1.fetchSandboxHubData)(accessToken, portalId, accountEnv);
            if (sandboxDataResponse.parentHubId) {
                parentAccountId = sandboxDataResponse.parentHubId;
            }
        }
    }
    catch (err) {
        // Log error but do not throw
        if ((0, errors_1.isHubSpotHttpError)(err)) {
            logger_1.logger.debug(err.message);
        }
        logger_1.logger.debug(err);
    }
    try {
        if (accountType === config_2.HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST) {
            const { data: developerTestAccountResponse } = await (0, developerTestAccounts_1.fetchDeveloperTestAccountData)(accessToken, portalId, accountEnv);
            if (developerTestAccountResponse) {
                parentAccountId = developerTestAccountResponse.parentPortalId;
            }
        }
    }
    catch (err) {
        // Log error but do not throw
        if ((0, errors_1.isHubSpotHttpError)(err)) {
            logger_1.logger.debug(err.message);
        }
        logger_1.logger.debug(err);
    }
    const updatedAccount = {
        accountId: portalId,
        accountType,
        personalAccessKey,
        name: name || account?.name,
        authType: auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
        auth: { tokenInfo: { accessToken, expiresAt } },
        parentAccountId,
        env: accountEnv,
    }; // Account may temporarily not have a name before prompted to add one in the CLI
    // Add new account if it doesn't exist, otherwise update existing account
    if (account) {
        (0, config_1.updateConfigAccount)(updatedAccount);
    }
    else {
        (0, config_1.addConfigAccount)(updatedAccount);
    }
    if (makeDefault) {
        (0, config_1.setConfigAccountAsDefault)(updatedAccount.accountId);
    }
    return updatedAccount;
}
exports.updateConfigWithAccessToken = updateConfigWithAccessToken;
